// Copyright 2016 Serilog Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.



// ReSharper disable VirtualMemberNeverOverridden.Global

namespace Serilog.Data;

/// <summary>
/// An abstract base class for visitors that walk data in the
/// <see cref="LogEventPropertyValue"/> format. Subclasses, by
/// overriding appropriate methods, may search for, transform,
/// or print the value structures being visited.
/// </summary>
/// <remarks>
/// Stateless, designed to accommodate allocation-free visiting of multiple
/// values by the same visitor instance.
/// </remarks>
/// <typeparam name="TState">The type of a state object passed through
/// the visiting process.</typeparam>
/// <typeparam name="TResult">The type of the result generated by visiting
/// a node.</typeparam>
public abstract class LogEventPropertyValueVisitor<TState, TResult>
{
    /// <summary>
    /// Visit the root node type. This method delegates to
    /// a concrete Visit*Value() method appropriate for the value.
    /// </summary>
    /// <param name="state">Operation state.</param>
    /// <param name="value">The value to visit.</param>
    /// <returns>The result of visiting <paramref name="value"/>.</returns>
    /// <exception cref="ArgumentNullException">When <paramref name="value"/> is <code>null</code></exception>
    protected virtual TResult Visit(TState state, LogEventPropertyValue value)
    {
        Guard.AgainstNull(value);

        if (value is ScalarValue sv)
            return VisitScalarValue(state, sv);

        if (value is SequenceValue seqv)
            return VisitSequenceValue(state, seqv);

        if (value is StructureValue strv)
            return VisitStructureValue(state, strv);

        if (value is DictionaryValue dictv)
            return VisitDictionaryValue(state, dictv);

        return VisitUnsupportedValue(state, value);
    }

    /// <summary>
    /// Visit a <see cref="ScalarValue"/> value.
    /// </summary>
    /// <param name="state">Operation state.</param>
    /// <param name="scalar">The value to visit.</param>
    /// <returns>The result of visiting <paramref name="scalar"/>.</returns>
    protected abstract TResult VisitScalarValue(TState state, ScalarValue scalar);

    /// <summary>
    /// Visit a <see cref="SequenceValue"/> value.
    /// </summary>
    /// <param name="state">Operation state.</param>
    /// <param name="sequence">The value to visit.</param>
    /// <returns>The result of visiting <paramref name="sequence"/>.</returns>
    protected abstract TResult VisitSequenceValue(TState state, SequenceValue sequence);

    /// <summary>
    /// Visit a <see cref="StructureValue"/> value.
    /// </summary>
    /// <param name="state">Operation state.</param>
    /// <param name="structure">The value to visit.</param>
    /// <returns>The result of visiting <paramref name="structure"/>.</returns>
    protected abstract TResult VisitStructureValue(TState state, StructureValue structure);

    /// <summary>
    /// Visit a <see cref="DictionaryValue"/> value.
    /// </summary>
    /// <param name="state">Operation state.</param>
    /// <param name="dictionary">The value to visit.</param>
    /// <returns>The result of visiting <paramref name="dictionary"/>.</returns>
    protected abstract TResult VisitDictionaryValue(TState state, DictionaryValue dictionary);

    /// <summary>
    /// Visit a value of an unsupported type. Always throws <see cref="NotSupportedException"/>, when is not overridden.
    /// </summary>
    /// <param name="state">Operation state.</param>
    /// <param name="value">The value to visit.</param>
    /// <returns>The result of visiting <paramref name="value"/>.</returns>
    /// <exception cref="ArgumentNullException">When <paramref name="value"/> is <code>null</code></exception>
    /// <exception cref="NotSupportedException">Always</exception>
    // ReSharper disable once UnusedParameter.Global
    protected virtual TResult VisitUnsupportedValue(TState state, LogEventPropertyValue value)
    {
        Guard.AgainstNull(value);
        throw new NotSupportedException($"The value {value} is not of a type supported by this visitor.");
    }
}
